package com.gc.cscott.cli;

import com.gc.cscott.fs.FileSystemWorker;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;

/**
 *
 * @author root
 */
public class ListLanguagesCommand implements Command {
    @Inject
    FileSystemWorker fsWorker;

    public List<String> listHelp() {
        return Arrays.asList(
                "Print all languages used by competitors");
    }

    public List<String> perform(List<String> args) {
        Map<String, Integer> extensionsMap = new TreeMap<String, Integer>();
        try {
            Map<String, String> plainSignaturesMap =ImmutableMap.<String, String>builder()
                            .put("This program cannot be run in DOS mode","win32-1")
                            .put("This program requires Microsoft Windows","win32-2")
                            .put("This program must be run under Win32","win32-3")
                            .put(".VisualStudio.","VisualStudio")
                            .put("TObject","~")
                            .put("XString","~")
                            .build();
            List<Pattern> regexpSignaturesList = ImmutableList.of(
                    Pattern.compile(".*\\|([a-z]*\\d+\\.\\d+\\.\\d+\\.\\d+)\\|.*", Pattern.CASE_INSENSITIVE),
                    Pattern.compile(".*\\|([a-z]*\\d+\\.\\d+\\.\\d+)\\|.*", Pattern.CASE_INSENSITIVE),
                    Pattern.compile(".*[^0-9](19\\d\\d)\\|.*", Pattern.CASE_INSENSITIVE),
                    Pattern.compile(".*([a-zA-z0-9 \\.]*Borland[a-zA-z0-9 \\.]*).*", Pattern.CASE_INSENSITIVE),
                    Pattern.compile(".*([a-zA-z0-9 ]*Microsoft[a-zA-z0-9 ]*).*", Pattern.CASE_INSENSITIVE),
                    Pattern.compile(".*(kernel[0-9.]*dll).*", Pattern.CASE_INSENSITIVE),
                    Pattern.compile(".*(mscorlib[0-9.]*(dll)?).*", Pattern.CASE_INSENSITIVE),
                    Pattern.compile(".*(ifstream).*", Pattern.CASE_INSENSITIVE),
                    Pattern.compile(".*(ofstream).*", Pattern.CASE_INSENSITIVE),
                    Pattern.compile(".*(_STL).*", Pattern.CASE_INSENSITIVE)
                    );


            for (File file : fsWorker.listCompetitorsFilesAll()) {
                String ext = FilenameUtils.getExtension(file.getName()).toLowerCase();
                if (ext.equals("exe")||ext.equals("ex_")){
                    byte[] header = FileUtils.readFileToByteArray(file);
                    int limit = Math.min(header.length, Integer.MAX_VALUE);
                    String signature = new String(header, 0, limit, "ASCII");
                    signature = signature.replaceAll("[^a-zA-z0-9 \\.\\\\]+", "|");
                    while(signature.equals(signature.replaceAll("[|].{1,2}[|]", "|"))==false){
                        signature = signature.replaceAll("[|].{1,2}[|]", "|");
                    }
                    Set<String> matchedWords = new TreeSet<String>();
                    for(Map.Entry<String, String> word:plainSignaturesMap.entrySet()){
                        if (signature.toLowerCase().contains(word.getKey().toLowerCase())){
                            matchedWords.add(word.getValue().equals("~")?word.getKey():word.getValue());
                        }
                    }
                    for(Pattern pattern:regexpSignaturesList){
                        //System.out.println("p = "+pattern.pattern());
                        Matcher m = pattern.matcher(signature);
                        //System.out.println("p "+pattern.pattern());
                        if (m.matches()){
                            if (m.groupCount()==0){
                                throw new Error("Wrong pattern (it must contain matching group): "+pattern.pattern());
                            }
                            for(int g=1;g<=m.groupCount();g++){
                                String group = m.group(g);
                                //System.out.println("  g = "+group);
                                if (group!=null){
                                    if (group.length()<signature.length()){
                                        matchedWords.add(group);
                                    }
                                }
                            }
                        }
                    }
                    //ext+=" "+matchedWords+" <<"+signature+">> "+file.getPath();
                    if (matchedWords.size()>0){
                        ext+=" "+matchedWords;
                    } else {
                        ext+=" <<"+signature+">> - "+file.getPath();
                    }
                }
                if (extensionsMap.containsKey(ext)==false){
                    extensionsMap.put(ext,0);
                }
                extensionsMap.put(ext, extensionsMap.get(ext)+1);
            }

            System.out.println("Used files extentions:");
            int index = 0;
            for (String ext : extensionsMap.keySet()) {
                System.out.printf("%2d. %s (%d times)", ++index, ext.equals("") ? "<empty>" : ext, extensionsMap.get(ext));
                System.out.println();
            }
            System.out.println("Done");
        } catch (IOException ex) {
            throw new Error(ex);
        }
        return args;
    }
}
